#ifndef _______steeringBehav__
#define _______steeringBehav__

#include "stdafx.h"
//#include <vector>
//#include <string>
//#include <list>

//#include "MyCommon.h"
#include "Path.h"
//#include <Ogre.h>

class Boid;
class Wall3D;

typedef MyCommon::Vector3 MyVec3;
typedef std::vector<Ogre::Entity*> EntityVector;
typedef EntityVector::iterator EntityIter;

//--------------------------- Constants ----------------------------------

//the radius of the constraining circle for the wander behavior
const double WanderRad    = 1.2;
//distance the wander circle is projected in front of the agent
const double WanderDist   = 2.0;
//the maximum amount of displacement along the circle each frame
const double WanderJitterPerSec = 80.0;
//used in path following
const double WaypointSeekDist   = 20;                                     
//-------------------------------------------------------------------------

class SteeringBehavior
{
public:
	enum summing_method{weighted_average, prioritized, dithered};

private:
	enum behavior_type
	{
		none               = 0x00000,
		seek               = 0x00002,
		flee               = 0x00004,
		arrive             = 0x00008,
		wander             = 0x00010,
		cohesion           = 0x00020,
		separation         = 0x00040,
		allignment         = 0x00080,
		obstacle_avoidance = 0x00100,
		wall_avoidance     = 0x00200,
		follow_path        = 0x00400,
		pursuit            = 0x00800,
		evade              = 0x01000,
		interpose          = 0x02000,
		hide               = 0x04000,
		flock              = 0x08000,
		offset_pursuit     = 0x10000,
	};

	//a pointer to the owner of this instance
	Boid* mBoid;
	//the steering force created by the combined effect of all
	//the selected behaviors
	MyVec3 mSteeringForce;
	//a vertex buffer to contain the feelers rqd for wall avoidance  
	std::vector<MyCommon::Ray> mFeelers;
	//the length of the 'feeler/s' used in wall detection
	MyReal mdWallDetectionFeelerLength;
	//the current position on the wander circle the agent is
	//attempting to steer towards
	MyVec3 mvWanderTarget;
	//length of the 'detection box' utilized in obstacle avoidance
	MyReal mdDBoxLength;

	//explained above
	MyReal mdWanderJitter;
	MyReal mdWanderRadius;
	MyReal mdWanderDistance;

	

	//multipliers. These can be adjusted to effect strength of the  
	//appropriate behavior. Useful to get flocking the way you require
	//for example.
	MyReal mdWeightSeparation;
	MyReal mdWeightCohesion;
	MyReal mdWeightAlignment;
	MyReal mdWeightWander;
	MyReal mdWeightObstacleAvoidance;
	MyReal mdWeightWallAvoidance;
	MyReal mdWeightSeek;
	MyReal mdWeightFlee;
	MyReal mdWeightArrive;
	MyReal mdWeightPursuit;
	MyReal mdWeightOffsetPursuit;
	MyReal mdWeightInterpose;
	MyReal mdWeightHide;
	MyReal mdWeightEvade;
	MyReal mdWeightFollowPath;

	//how far the agent can 'see'
	MyReal mdViewDistance;

	//pointer to any current path
	Path* mpPath;

	//the distance (squared) a Boid has to be from a path waypoint before
	//it starts seeking to the next waypoint
	MyReal mdWaypointSeekDistSq;

	//any offset used for formations or offset pursuit
	MyVec3 mvOffset;

	//binary flags to indicate whether or not a behavior should be active
	int miFlags;

	//Arrive makes use of these to determine how quickly a Boid
	//should decelerate to its target
	enum Deceleration{slow = 3, normal = 2, fast = 1};

	//default
	Deceleration mDeceleration;

	//what type of method is used to sum any active behavior
	summing_method  mSummingMethod;

	//this function tests if a specific bit of miFlags is set
	bool On(behavior_type bt){return (miFlags & bt) == bt;}

	bool AccumulateForce(MyVec3 &sf, MyVec3 ForceToAdd);

	//creates the antenna utilized by the wall avoidance behavior
	void CreateFeelers();

	/* .......................................................

	BEGIN BEHAVIOR DECLARATIONS

	.......................................................*/
	//this behavior moves the agent towards a target position
	MyVec3 Seek(MyVec3 TargetPos);
	//this behavior returns a vector that moves the agent away
	//from a target position
	MyVec3 Flee(MyVec3 TargetPos);
	//this behavior is similar to seek but it attempts to arrive 
	//at the target position with a zero velocity
	MyVec3 Arrive(MyVec3 TargetPos,Deceleration deceleration);
	//this behavior predicts where an agent will be in time T and seeks
	//towards that point to intercept it.
	MyVec3 Pursuit(const Boid* agent);
	//this behavior maintains a position, in the direction of offset
	//from the target Boid
	MyVec3 OffsetPursuit(const Boid* agent, const MyVec3 offset);
	//this behavior attempts to evade a pursuer
	MyVec3 Evade(const Boid* agent);
	//this behavior makes the agent wander about randomly
	MyVec3 Wander();
	//this returns a steering force which will attempt to keep the agent 
	//away from any obstacles it may encounter
	MyVec3 ObstacleAvoidance(const EntityVector& obstacles);
	//this returns a steering force which will keep the agent away from any
	//walls it may encounter
	MyVec3 WallAvoidance(const std::vector<Wall3D> &walls);
	//given a series of MyVec3s, this method produces a force that will
	//move the agent along the waypoints in order
	MyVec3 FollowPath();
	//this results in a steering force that attempts to steer the Boid
	//to the center of the vector connecting two moving agents.
	MyVec3 Interpose(const Boid* BoidA, const Boid* BoidB);
	//given another agent position to hide from and a list of BaseGameEntitys this
	//method attempts to put an obstacle between itself and its opponent
	MyVec3 Hide(const Boid* hunter, const EntityVector& obstacles);

	// -- Group Behaviors -- //
	MyVec3 Cohesion(const std::vector<Boid*> &agents);
	MyVec3 Separation(const std::vector<Boid*> &agents);
	MyVec3 Alignment(const std::vector<Boid*> &agents);

	//the following three are the same as above but they use cell-space
	//partitioning to find the neighbors
	MyVec3 CohesionPlus(const std::vector<Boid*> &agents);
	MyVec3 SeparationPlus(const std::vector<Boid*> &agents);
	MyVec3 AlignmentPlus(const std::vector<Boid*> &agents);

	/* .......................................................

	END BEHAVIOR DECLARATIONS

	.......................................................*/

	//calculates and sums the steering forces from any active behaviors
	MyVec3 CalculateWeightedSum();
	MyVec3 CalculatePrioritized();
	MyVec3 CalculateDithered();

	//helper method for Hide. Returns a position located on the other
	//side of an obstacle to the pursuer
	MyVec3 GetHidingPosition(const MyVec3& posOb,
		const double radiusOb,
		const MyVec3& posHunter);

public:


	SteeringBehavior(Boid* agent);
	~SteeringBehavior();

	//calculates and sums the steering forces from any active behaviors
	MyVec3 Calculate();

	//calculates the component of the steering force that is parallel
	//with the vehicle heading
	MyReal ForwardComponent();

	//calculates the component of the steering force that is perpendicular
	//with the vehicle heading
	MyReal SideComponent();

	void SetPath(std::list<MyVec3> new_path){mpPath->Set(new_path);}
	void CreateRandomPath(int num_waypoints, int mx, int my, int cx, int cy)const
	{	mpPath->CreateRandomPath(num_waypoints, mx, my, cx, cy);	}

	MyVec3 getForce()const{return mSteeringForce;}

	void SetSummingMethod(summing_method sm){mSummingMethod = sm;}

	void FleeOn(){miFlags |= flee;}
	void SeekOn(){miFlags |= seek;}
	void ArriveOn(){miFlags |= arrive;}
	void WanderOn(){miFlags |= wander;}
	//void PursuitOn(Vehicle* v){miFlags |= pursuit; mpTargetAgent1 = v;}
	//void EvadeOn(Vehicle* v){miFlags |= evade; mpTargetAgent1 = v;}
	void CohesionOn(){miFlags |= cohesion;}
	void SeparationOn(){miFlags |= separation;}
	void AlignmentOn(){miFlags |= allignment;}
	void ObstacleAvoidanceOn(){miFlags |= obstacle_avoidance;}
	void WallAvoidanceOn(){miFlags |= wall_avoidance;}
	void FollowPathOn(){miFlags |= follow_path;}
	//void InterposeOn(Vehicle* v1, Vehicle* v2){miFlags |= interpose; mpTargetAgent1 = v1; mpTargetAgent2 = v2;}
	//void HideOn(Vehicle* v){miFlags |= hide; mpTargetAgent1 = v;}
	//void OffsetPursuitOn(Vehicle* v1, const Vector2 offset){miFlags |= offset_pursuit; mvOffset = offset; mpTargetAgent1 = v1;}  
	void FlockingOn(){CohesionOn(); AlignmentOn(); SeparationOn(); WanderOn();}

	void FleeOff()  {if(On(flee))   miFlags ^=flee;}
	void SeekOff()  {if(On(seek))   miFlags ^=seek;}
	void ArriveOff(){if(On(arrive)) miFlags ^=arrive;}
	void WanderOff(){if(On(wander)) miFlags ^=wander;}
	void PursuitOff(){if(On(pursuit)) miFlags ^=pursuit;}
	void EvadeOff(){if(On(evade)) miFlags ^=evade;}
	void CohesionOff(){if(On(cohesion)) miFlags ^=cohesion;}
	void SeparationOff(){if(On(separation)) miFlags ^=separation;}
	void AlignmentOff(){if(On(allignment)) miFlags ^=allignment;}
	void ObstacleAvoidanceOff(){if(On(obstacle_avoidance)) miFlags ^=obstacle_avoidance;}
	void WallAvoidanceOff(){if(On(wall_avoidance)) miFlags ^=wall_avoidance;}
	void FollowPathOff(){if(On(follow_path)) miFlags ^=follow_path;}
	void InterposeOff(){if(On(interpose)) miFlags ^=interpose;}
	void HideOff(){if(On(hide)) miFlags ^=hide;}
	void OffsetPursuitOff(){if(On(offset_pursuit)) miFlags ^=offset_pursuit;}
	void FlockingOff(){CohesionOff(); AlignmentOff(); SeparationOff(); WanderOff();}

	bool isFleeOn(){return On(flee);}
	bool isSeekOn(){return On(seek);}
	bool isArriveOn(){return On(arrive);}
	bool isWanderOn(){return On(wander);}
	bool isPursuitOn(){return On(pursuit);}
	bool isEvadeOn(){return On(evade);}
	bool isCohesionOn(){return On(cohesion);}
	bool isSeparationOn(){return On(separation);}
	bool isAlignmentOn(){return On(allignment);}
	bool isObstacleAvoidanceOn(){return On(obstacle_avoidance);}
	bool isWallAvoidanceOn(){return On(wall_avoidance);}
	bool isFollowPathOn(){return On(follow_path);}
	bool isInterposeOn(){return On(interpose);}
	bool isHideOn(){return On(hide);}
	bool isOffsetPursuitOn(){return On(offset_pursuit);}

	//double DBoxLength()const{return mdDBoxLength;}
	const std::vector<MyCommon::Ray>& GetFeelers()const{return mFeelers;}

	MyReal WanderJitter()const{return mdWanderJitter;}
	MyReal WanderDistance()const{return mdWanderDistance;}
	MyReal WanderRadius()const{return mdWanderRadius;}

	MyReal SeparationWeight()const{return mdWeightSeparation;}
	MyReal AlignmentWeight()const{return mdWeightAlignment;}
	MyReal CohesionWeight()const{return mdWeightCohesion;}

	
};

#endif